home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Development Platforms / Apple II / Essentials / Technical.Notes / IIGS / TN.IIGS.087 < prev    next >
Encoding:
Text File  |  1990-09-21  |  23.6 KB  |  634 lines  |  [TEXT/pdos]

  1. Apple II
  2. Technical Notes
  3. _____________________________________________________________________________
  4.                                                   Developer Technical Support
  5.  
  6. Apple IIgs
  7. #87:    Patching the Tool Dispatcher
  8.  
  9. Written by:    Mike Lagae and Dave Lyons                       September 1990
  10.  
  11. This Technical Note presents the Apple standard way to patch into the Apple 
  12. IIgs Tool Dispatcher vectors.
  13. _____________________________________________________________________________
  14.  
  15. This Note presents MPW IIgs assembly-language code which provides the Apple-
  16. standard way for utilities to patch and unpatch the Tool Dispatcher vectors.  
  17. If all Tool Dispatcher patches follow this protocol, patches can be installed 
  18. and removed in any order, without ever accidentally unpatching somebody who 
  19. patched in after the one getting removed.
  20.  
  21. Using this protocol, each patch begins with a header in a standard form--a form 
  22. recognizable by these routines (see PatchHeader).  This way routines (like 
  23. RemoveE10000) can scan through the list of patches and remove one from the 
  24. middle.
  25.  
  26. If your patch is going to stay in the system until shutdown, use this standard 
  27. patch protocol anyway.  This way other utilities can still recognize your 
  28. patch and scan past it to find the next one.  This Note is not just to show 
  29. you a way to patch the tool dispatcher--it's to show you the way.  If you patch 
  30. tool dispatcher vectors in any other way, you strip other utilities of their 
  31. ability to remove their patches.
  32.  
  33. Of course, patching the Tool Dispatcher vectors slows down all toolbox calls, 
  34. so you shouldn't patch the tool dispatcher without a pretty good reason.  If 
  35. you need to patch a toolbox function, it is usually better to do it by 
  36. modifying a tool set's function pointer table instead of patching the 
  37. dispatcher.
  38.  
  39. The code in this note is specific to the System tool dispatch vectors ($E10000 
  40. and $E10004), but the same technique is recommended for the User tool dispatch 
  41. vectors--just change $E10000 to $E10008, $E10004 to $E1000C, and 
  42. ToolPointerTable to UserToolPointerTable.
  43.  
  44.  
  45. What Is This Stuff?
  46.  
  47. This Note presents the following four routines.
  48.  
  49. PatchHeader is the simplest patch function that obeys the protocol.  This is 
  50. where you put your own patch code.
  51.  
  52. InstallE10000 installs a patch into the patch chain.  For example:
  53.  
  54.        pushlong #PatchHeader
  55.        jsl InstallE10000
  56.        ply
  57.        ply                    ;remove the input parameter
  58.        bcc noError            ;error in A
  59.  
  60. RemoveE10000 removes a patch from the patch chain.  For example:
  61.  
  62.        pushlong #PatchHeader
  63.        jsl RemoveE10000
  64.        ply
  65.        ply                    ;remove the input parameter
  66.        bcc noError            ;error in A
  67.  
  68. CheckPatch determines whether the specified address is the starting address of 
  69. a standard patch.  For example:
  70.  
  71.        pushlong #PatchHeader
  72.        jsl CheckPatch
  73.        ply
  74.        ply                    ;remove the input parameter
  75.        bcc validPatch
  76.  
  77. First, here are some comments and global equates.
  78.  
  79. ****************************************************************************
  80. *
  81. * Patch.e10000 - Routines to patch into the toolbox dispatch
  82. *                vectors at $e10000 and $e10004.
  83. *
  84. * By Michael Lagae
  85. * Software Quality Assurance
  86. * GS Toolbox Test Team
  87. *
  88. * July 18, 1989
  89. *
  90. * Written for the MPW IIGS Assembler -- Version 1.1b1, 4/9/90
  91. *
  92. * Copyright 1989-1990 by Apple Computer, Inc.
  93. *
  94. ****************************************************************************
  95.  
  96.        case yes
  97.        machine M65816
  98.        string asis
  99.        msb on
  100.        print on
  101.  
  102.        export CheckPatch                 ; Check for a valid patch header.
  103.        export InstallE10000              ; Adds a patch into the toolbox 
  104. ;                                          vectors.
  105.        export RemoveE10000               ; Removes a toolbox dispatch vector 
  106. ;                                          patch.
  107.        export PatchHeader                ; The simplest toolbox dispatch 
  108. ;                                          vector patch.
  109.  
  110. ****************************************************************************
  111. * Equates - Various equates required by these routines.
  112. *
  113. versionNumber         equ $0100          ; The version number of this library.
  114.  
  115. dispatch1             equ $e10000        ; The first toolbox dispatch vector.
  116. dispatch2             equ $e10004        ; The second toolbox dispatch vector.
  117. ToolPointerTable      equ $e103c0        ; Pointer to the active System TPT.
  118. UserToolPointerTable  equ $e103c8        ; Pointer to the active User TPT.
  119.  
  120. ; Error return values from the routines InstallE10000 and RemoveE10000
  121.  
  122. noError               equ $0000          ; Value returned if no error occurs.
  123. badHeaderError        equ $8001          ; Patch header wasn't valid.
  124. headerNotFoundError   equ $8002          ; Header to remove wasn't in the 
  125. ;                                          linked list.
  126.  
  127. PatchHeader is the standard shell for the actual patch code.  Your code goes 
  128. in here, at NewDispatch2.  When you get control at NewDispatch2, the function 
  129. number is in X and there are two RTL addresses on the stack (pushed after the 
  130. function's parameters).
  131.  
  132. Your patch code does not care whether the tool call is being made through the 
  133. $E10000 or $E10004 vector--in either case you get control with two RTL 
  134. addresses on the stack.
  135.  
  136. ****************************************************************************
  137. * PatchHeader - Header required of all routines that will be patched
  138. *               into the toolbox dispatch vectors.
  139. *
  140. * Note:  The code between next1Vector and NewDispatch2 must be included
  141. *        for all calls.  The code below NewDispatch2 only needs to be
  142. *        included for patches that want to post patch the calls.
  143. *
  144. PatchHeader proc
  145.        entry next1Vector,next2Vector
  146.        entry dispatch1Vector,dispatch2Vector
  147.        entry NewDispatch1,NewDispatch2
  148.  
  149. next1Vector                              ; Where dispatch1 should go when 
  150. ;                                          finished.
  151.        jml next1Vector                   ; (Filled in by InstallE10000).
  152. next2Vector                              ; Where dispatch2 should go when 
  153. ;                                          finished.
  154.        jml next2Vector                   ; (Filled in by InstallE10000).
  155. dispatch1Vector                          ; Holds the JML instruction from 
  156. ;                                          $e10000.
  157.        jml dispatch1Vector               ; (Filled in by InstallE10000).
  158. dispatch2Vector                          ; Holds the JML instruction from 
  159. ;                                          $e10004.
  160.        jml dispatch2Vector               ; (Filled in by InstallE10000).
  161.  
  162. anRtl  rtl                               ; An RTL instruction.  Its address 
  163. ;                                          will be
  164.                                          ; pushed on the stack for dispatch1 
  165. ;                                          calls.
  166.  
  167. NewDispatch1                             ; Entry point for dispatch1 toolbox 
  168. ;                                          vector.
  169.  
  170.        phk                               ; Push program bank.
  171.        pea anRtl-1                       ; Push the address of a RTL.
  172.  
  173. NewDispatch2                             ; Entry point for dispatch2 toolbox 
  174. ;                                          vector.
  175.  
  176. ; The following code should be included in the PatchHeader if the patch wants
  177. ; to perform post patching.  This code will determine if the call that was 
  178. ; made actually exists and if it does, post patching can occur.  If the call 
  179. ; doesn't exist, any pre-call routines can be executed, but the post patching 
  180. ; shouldn't be attempted because the dispatcher will remove the second return 
  181. ; address from the stack, thus not returning to your post patching routines.
  182. ; Stack equates for this routine.
  183.  
  184. aLong  equ $0001                    ; A temporary long value.
  185. oldDP  equ aLong+4                  ; Where the direct page is saved to.
  186. oldTM  equ oldDP+2                  ; Where the tool call number is saved.
  187.  
  188.        phx                          ; Save the call that's being made.
  189.        phd                          ; Save the current direct page.
  190.        lda >ToolPointerTable+2      ; Get the TPT to determine the number
  191.        pha                          ; of tool sets loaded.
  192.        lda >ToolPointerTable
  193.        pha
  194.        tsc                          ; Set the direct page to the stack.
  195.        tcd
  196.        txa                          ; See if this tool set exits.
  197.        and #$00ff
  198.        cmp [aLong]                  ; Is it larger than the number of tool 
  199. ;                                     sets?
  200.        bcs @noCall                  ; JIF this tool set doesn't exist.
  201.        asl a
  202.        asl a
  203.        tay                          ; Now get the pointer to the FPT.
  204.        lda [aLong],y
  205.        tax
  206.        iny
  207.        iny
  208.        lda [aLong],y
  209.        sta aLong+2
  210.        stx aLong
  211.        lda oldTM                    ; Get the function number.
  212.        and #$ff00
  213.        xba
  214.        cmp [aLong]                  ; Compare it to the number of entries in 
  215. ;                                     table.
  216. @noCall
  217.        pla                          ; Remove aLong from the stack.
  218.        pla
  219.        pld                          ; Restore the original direct page.
  220.        plx                          ; Recover the tool number.
  221.  
  222. ; At this point the carry flag is set if the tool call doesn't exist and clear
  223. ; if the tool call exits.  No post patching must occur if the carry flag is 
  224. ; set.
  225.  
  226.        jmp next2Vector              ; Go to the original $e10004 jump 
  227. ;                                     instruction.
  228.  
  229.        endp
  230.  
  231. ****************************************************************************
  232. * CheckPatch - Checks the passed toolbox dispatch vector to see if it
  233. *              points to a valid patch.
  234. *
  235. * Input: Passed via the stack following C conventions.
  236. *     newPatchAddr (long) - Address of the patch routine.
  237. *
  238. * Output:
  239. *     If newPatchAddr is a valid patch -
  240. *          Carry clear
  241. *     If newPatchAddr is not a valid patch -
  242. *          Carry set
  243. *
  244. CheckPatch proc
  245.  
  246. zprtl         equ $01               ; The address for the rtl on our direct 
  247. ;                                     page.
  248. newPatchAddr  equ zprtl+3           ; Address of patch (parameter to this 
  249. ;                                     routine).
  250.  
  251.        tsc                          ; Make the stack the direct page after 
  252. ;                                     saving
  253.        phd                          ; the current direct page.
  254.        tcd
  255.  
  256.  
  257.        lda newPatchAddr+2           ; Simple check to check for a valid 
  258. ;                                     pointer.
  259.        and #$ff00
  260.        bne BadPatch                 ; Wasn't zero, can't be a valid pointer.
  261.  
  262.        lda [newPatchAddr]           ; Check for the first JML instruction.
  263.        and #$00ff
  264.        cmp #$005c
  265.        bne BadPatch
  266.  
  267.        ldy #$04                     ; Check for the second JML instruction.
  268.        lda [newPatchAddr],y
  269.        and #$00ff
  270.        cmp #$005c
  271.        bne BadPatch
  272.  
  273.        ldy #$08                     ; Check for the third JML instruction.
  274.        lda [newPatchAddr],y
  275.        and #$00ff
  276.        cmp #$005c
  277.        bne BadPatch
  278.  
  279.        ldy #$0c                     ; Check for the fourth JML instruction.
  280.        lda [newPatchAddr],y
  281.        and #$00ff
  282.        cmp #$005c
  283.        bne BadPatch
  284.  
  285.        ldy #$10                     ; Check for the rtl and phk instructions.
  286.        lda [newPatchAddr],y
  287.        cmp #$4b6b
  288.        bne BadPatch
  289.  
  290.        iny                          ; Check for the phk and pea instructions.
  291.        lda [newPatchAddr],y
  292.        cmp #$f44b
  293.        bne BadPatch
  294.  
  295.        clc                          ; Calculate the address of the rtl 
  296. ;                                     instruction.
  297.        lda newPatchAddr
  298.        adc #$000f
  299.        ldy #$13                     ; Check for address of the rtl 
  300. ;                                     instruction.
  301.        cmp [newPatchAddr],y
  302.        bne BadPatch
  303.  
  304. GoodPatch
  305.        pld                          ; Restore the direct page and report
  306.        clc                          ; that it was a good patch.
  307.        rtl
  308.  
  309. BadPatch
  310.        pld                          ; Restore the direct page and report
  311.        sec                          ; that something was wrong.
  312.        rtl
  313.  
  314.        endp
  315.  
  316. ****************************************************************************
  317. * InstallE10000   - Sets the jump vector at $e10000 and $e10004 to point to
  318. *                 the passed new toolbox dispatch vector patch.  This routine
  319. *                 also updates the linked lists so that more than one routine
  320. *                 can be patched into the dispatch vectors.
  321. *
  322. * Input: Passed via the stack following C conventions.
  323. *     newPatchAddr (long) - Address of the patch routine.
  324. *
  325.  
  326. * Output:
  327. *     If an error occurred -
  328. *          Carry set, Accumulator contains one of the following error codes:
  329. *               badHeaderError
  330. *     If no error occurred and patch was installed successfully -
  331. *          Carry clear, Accumulator contains zero.
  332. *
  333. InstallE10000 proc
  334.  
  335. oldPatchAddr    equ $01                   ; Address of existing patch.
  336. zprtl           equ oldPatchAddr+4        ; The address for the rtl.
  337. zpsize          equ zprtl-oldPatchAddr    ; Size of direct page we'll have on 
  338. ;                                           the stack.
  339. newPatchAddr    equ zprtl+3               ; Address of patch (parameter to 
  340. ;                                           this routine).
  341.  
  342.        tsc                          ; Move the stack pointer to point beyond
  343.        sec                          ; the direct page variables that we'll
  344.        sbc #zpsize                  ; place on the stack.
  345.        tcs
  346.        phd                          ; Save the direct page register.
  347.        tcd                          ; Set the direct page.
  348.        php                          ; Disable interrupts
  349.        sei
  350.  
  351.        pei newPatchAddr+2           ; Check if patch header is valid.
  352.        pei newPatchAddr
  353.        jsl CheckPatch
  354.        plx                          ; Remove the parameters from the stack.
  355.        plx
  356.        bcc @1                       ; Report the badHeaderError if detected.
  357.        ldy #badHeaderError
  358.        jmp Exit
  359.  
  360. @1     lda >dispatch1               ; Set up the next1Vector in the new patch.
  361.        sta [newPatchAddr]           ; The JML instruction and low byte.
  362.        lda >dispatch1+2
  363.        ldy #$02
  364.        sta [newPatchAddr],y         ; The middle and upper bytes.
  365.  
  366.        lda >dispatch2               ; Set up the next2Vector in the new patch.
  367.        ldy #$04
  368.        sta [newPatchAddr],y         ; The JML instruction and low byte.
  369.        lda >dispatch2+2
  370.        ldy #$06
  371.        sta [newPatchAddr],y         ; The middle and upper bytes.
  372.  
  373.        lda >dispatch1+3             ; See if there's already a patch in 
  374. ;                                     dispatch1.
  375.        and #$00ff
  376.        sta oldPatchAddr+2
  377.        pha                          ; High byte of possible header address.
  378.        lda >dispatch1+1
  379.        sec
  380.        sbc #$0011
  381.        sta oldPatchAddr
  382.        pha                          ; Low byte of possible header address.
  383.        jsl CheckPatch
  384.        plx
  385.        plx
  386.        bcs First                    ; JIF this will be the first patch 
  387. ;                                     installed.
  388.  
  389.        ldy #$08                     ; Set up the dispatch1Vector in the new 
  390. ;                                     patch.
  391.        lda [oldPatchAddr],y
  392.        sta [newPatchAddr],y         ; The JML instruction and low byte.
  393.        ldy #$0a
  394.        lda [oldPatchAddr],y
  395.        sta [newPatchAddr],y         ; The middle and upper bytes.
  396.  
  397.        ldy #$0c                     ; Set up the dispatch2Vector in the new 
  398. ;                                     patch.
  399.        lda [oldPatchAddr],y
  400.        sta [newPatchAddr],y         ; The JML instruction and low byte.
  401.        ldy #$0e
  402.        lda [oldPatchAddr],y
  403.        sta [newPatchAddr],y         ; The middle and upper bytes.
  404.        
  405.        bra PatchIt                  ; Now patch dispatch1 and dispatch2.
  406.  
  407. First  ldy #$08                     ; Set up the dispatch1Vector in the new 
  408. ;                                     patch.
  409.        lda >dispatch1
  410.        sta [newPatchAddr],y         ; The JML instruction and low byte.
  411.        ldy #$0a
  412.        lda >dispatch1+2
  413.        sta [newPatchAddr],y         ; The middle and upper bytes.
  414.  
  415.        ldy #$0c                     ; Set up the dispatch2Vector in the new 
  416. ;                                     patch.
  417.        lda >dispatch2
  418.        sta [newPatchAddr],y         ; The JML instruction and low byte.
  419.        ldy #$0e
  420.        lda >dispatch2+2
  421.        sta [newPatchAddr],y         ; The middle and upper bytes.
  422.  
  423. PatchIt
  424.        clc                          ; Calculate the address of the new 
  425. ;                                     dispatch2.
  426.        lda newPatchAddr
  427.        adc #$0015
  428.        sta newPatchAddr
  429.        xba
  430.        and #$ff00                   ; Mask in the JML instruction.
  431.        ora #$005c
  432.        sta >dispatch2               ; The JML instruction and low byte.
  433.        lda newPatchAddr+1
  434.        sta >dispatch2+2             ; The middle and upper bytes.
  435.  
  436.        sec                          ; Calculate the address of the new 
  437. ;                                     dispatch1.
  438.        lda newPatchAddr
  439.        sbc #$0004
  440.        sta newPatchAddr
  441.        xba
  442.        and #$ff00                   ; Mask in the JML instruction.
  443.        ora #$005c
  444.        sta >dispatch1               ; The JML instruction and low byte.
  445.        lda newPatchAddr+1
  446.        sta >dispatch1+2             ; The middle and upper bytes.
  447.  
  448.        ldy #noError                 ; Report that all went well.
  449.  
  450. Exit   plp                          ; Restore the interrupt state.
  451.        pld                          ; Restore the previous direct page 
  452. ;                                     register.
  453.        tsc                          ; Restore the stack pointer.
  454.        clc
  455.        adc #zpsize
  456.        tcs
  457.        tya                          ; Value to return.
  458.        beq @noerr
  459.        sec                          ; Report that there was an error.
  460.        rtl
  461. @noerr clc                          ; Report that there was no error.
  462.        rtl
  463.  
  464.        endp
  465.  
  466.  
  467. ****************************************************************************
  468. * RemoveE10000 - Removes the specified patch from the dispatch1 and dispatch2
  469. *                vectors and updates the linked lists for the remaining
  470. *                toolbox patches.
  471. *
  472. * Input: Passed via the stack following C conventions.
  473. *     patchToRemove (long) - Address of the patch to remove.
  474. *
  475. * Output:
  476. *     If an error occurred -
  477. *          Carry set, Accumulator contains one of the following error codes:
  478. *               badHeaderError
  479. *               headerNotFoundError
  480. *     If no error occurred and patch was removed successfully -
  481. *          Carry clear, Accumulator contains zero.
  482. *
  483. RemoveE10000 proc
  484.  
  485. patchDispAddr    equ $01                    ; Address of existing patch (and 1 
  486. ;                                             extra byte).
  487. prevHeader       equ patchDispAddr+5        ; Used to search through the 
  488. ;                                             linked list.
  489. zprtl            equ prevHeader+4           ; The address for the rtl.
  490. zpsize           equ zprtl-patchDispAddr    ; Size of direct page we'll have 
  491. ;                                             on the stack.
  492. patchToRemove    equ zprtl+3                ; Address of patch (parameter to 
  493. ;                                             this routine).
  494.  
  495.        tsc                          ; Move the stack pointer to point beyond
  496.        sec                          ; the direct page variables that we'll
  497.        sbc #zpsize                  ; place on the stack.
  498.        tcs
  499.        phd                          ; Save the direct page register.
  500.        tcd                          ; Set the direct page.
  501.        php                          ; Disable interrupts
  502.        sei
  503.  
  504.        pei patchToRemove+2          ; Check if patch header we were asked to
  505.        pei patchToRemove            ; remove is a valid header.
  506.        jsl CheckPatch
  507.        plx                          ; Remove the parameters from the stack.
  508.        plx
  509.        bcc @1                       ; Report the badHeaderError if detected.
  510.        ldy #badHeaderError
  511.        jmp Exit
  512.  
  513. @1     clc                          ; Create the JML instruction that would 
  514. ;                                     exist
  515.        lda patchToRemove            ; if the patchToRemove was installed.
  516.        adc #$0011
  517.        sta patchDispAddr+1
  518.        lda patchToRemove+2
  519.        sta patchDispAddr+3
  520.        lda patchDispAddr            ; Mask in the JML instruction.
  521.        and #$ff00
  522.        ora #$005c
  523.        sta patchDispAddr
  524.  
  525.        cmp >dispatch1               ; Check if the patch to remove is the 
  526. ;                                     first
  527.        bne NotFirstOne              ; patch installed.
  528.        lda >dispatch1+2
  529.        cmp patchDispAddr+2
  530.        bne NotFirstOne
  531.  
  532.        lda [patchToRemove]          ; Restore the Dispatch1 vector.
  533.        sta >dispatch1
  534.        ldy #$02
  535.        lda [patchToRemove],y
  536.        sta >dispatch1+2
  537.  
  538.  
  539.        ldy #$04                     ; Restore the Dispatch2 vector.
  540.        lda [patchToRemove],y
  541.        sta >dispatch2
  542.        ldy #$06
  543.        lda [patchToRemove],y
  544.        sta >dispatch2+2
  545.  
  546.        bra NoErr                    ; Everything went well.
  547.  
  548. NotFirstOne
  549.        sec                          ; Assume that whatever is in dispatch1 is 
  550.        lda >dispatch1+1             ; patch and get the address of its header.
  551.        sbc #$0011
  552.        sta prevHeader               ; Low and middle bytes.
  553.        lda >dispatch1+3
  554.        and #$00ff
  555.        sta prevHeader+2             ; Upper byte of header address.
  556.  
  557. @loop  pei prevHeader+2             ; Check if it really is a valid header.
  558.        pei prevHeader
  559.        jsl CheckPatch
  560.        plx                          ; Remove the parameters from the stack.
  561.        plx
  562.        bcc @2                       ; Report that the patch that we asked to
  563.        ldy #headerNotFoundError     ; remove wasn't found.
  564.        bra Exit
  565.  
  566. @2     lda [prevHeader]             ; See if this patch points to patch we 
  567.        cmp patchDispAddr            ; want to remove.
  568.        bne @nope
  569.        ldy #$02
  570.        lda [prevHeader],y
  571.        cmp patchDispAddr+2
  572.        bne @nope
  573.  
  574.        lda [patchToRemove]          ; Restore the next1Vector.
  575.        sta [prevHeader]
  576.        ldy #$02
  577.        lda [patchToRemove],y
  578.        sta [prevHeader],y
  579.  
  580.        ldy #$04                     ; Restore the next2Vector.
  581.        lda [patchToRemove],y
  582.        sta [prevHeader],y
  583.        ldy #$06
  584.        lda [patchToRemove],y
  585.        sta [prevHeader],y
  586.  
  587.        bra NoErr                    ; Everything went well.
  588.  
  589. @nope  ldy #$02                     ; Get the address of the next patch 
  590. ;                                     header.
  591.        lda [prevHeader],y
  592.        tax
  593.        lda [prevHeader]
  594.        sta prevHeader
  595.        stx prevHeader+2
  596.  
  597.        sec
  598.        lda prevHeader+1
  599.        sbc #$11
  600.        sta prevHeader
  601.        lda prevHeader+3
  602.        and #$00ff
  603.        sta prevHeader+2
  604.  
  605.  
  606.        bra @loop                    ; Now check this header.
  607.  
  608. NoErr  ldy #noError                 ; Report that all went well.
  609.  
  610. Exit   plp                          ; Restore the interrupt state.
  611.        pld                          ; Restore the previous direct page 
  612. ;                                     register.
  613.        tsc                          ; Restore the stack pointer.
  614.        clc
  615.        adc #zpsize
  616.        tcs
  617.        tya                          ; Value to return.
  618.        beq @noerr
  619.        sec                          ; Report that there was an error.
  620.        rtl
  621. @noerr clc                          ; Report that there was no error.
  622.        rtl
  623.  
  624.        endp
  625.  
  626.        end
  627.  
  628.  
  629. Further Reference
  630. _____________________________________________________________________________
  631.   o  Apple IIgs Toolbox Reference
  632.   o  Apple IIgs Technical Note #73, Using User Tool Sets
  633.  
  634.